use std::cell::RefCell;
use std::rc::Rc;

use crate::Syntax::environment::Environment;

use super::token::SyntaxToken;
use super::{statements::{Stmt, FunctionStmts}, callable::SyntaxCallable, interpreter::Interpreter, object::Object, syntaxResult::SyntaxResult};

pub struct SyntaxFunction {
    //declaration: Rc<RefCell<FunctionStmts>>,

    pub name:SyntaxToken,
    pub params:Rc<Vec<SyntaxToken>>,
    pub body:Rc<Vec<Stmt>>,
}

impl SyntaxFunction {
    pub fn new(_declaration: &FunctionStmts) -> Self {

        Self {
            name:_declaration.name.clone(),
            params:Rc::clone(&_declaration.params),
            body:Rc::clone(&_declaration.body)
        }
    }
}

impl  SyntaxCallable for SyntaxFunction {
    fn Call(&self, _interpreter:&Interpreter, _arguments: Vec<super::object::Object>) -> Result<Object, SyntaxResult> {

        let mut e = Environment::newWithEnclosing(Rc::clone(&_interpreter.globals));

        
        for (param, arg) in self.params.iter().zip(_arguments.iter()){
            e.define(param.as_string(), arg.clone());
        }
        //_interpreter.executeBlock(&self.body, e)?;

        match _interpreter.executeBlock(&self.body, e) {
            Err(SyntaxResult::Return { value }) => Ok(value),
            Err(e) => Err(e),
            Ok(_) => Ok(Object::Nil),
        }
    }

    fn arity(&self) -> usize {
        self.params.len()
    }
    fn toString(&self) ->String{
        self.name.as_string().into()
    }
}